{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> Introduction to Python 3 </center>\n",
    "\n",
    "<br>\n",
    "<center>![2vs3](https://community-cdn-digitalocean-com.global.ssl.fastly.net/assets/tutorials/images/large/Python_2_vs_3.jpg?1480953022)</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# What's new \n",
    "\n",
    "- A lot more than you might realize.\n",
    "- New syntax and keywords.\n",
    "- Additions to existing standard library modules, packages, classes, and functions.\n",
    "- A dozen completely new modules in the standard library.\n",
    "- Reliability improvements that eliminate hidden race conditions, enhance security, remove deterministic but surprising behaviors,\n",
    "- [https://docs.python.org/3/whatsnew/](https://docs.python.org/3/whatsnew/)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Unicode and bytes\n",
    "\n",
    "- In Python 2, str acts like bytes of data.\n",
    "\n",
    "- There is also unicode type to represent Unicode strings.\n",
    "\n",
    "- In Python 3, str is a string.\n",
    "\n",
    "- bytes are bytes.\n",
    "\n",
    "- There is no unicode. str strings are Unicode.\n",
    "\n",
    "- from `__future__` import unicode_literals"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "苹果派\n",
      "😁\n",
      "hehe\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Unicode variable names (PEP 3131)\n",
    "\n",
    "import math\n",
    "\n",
    "def 加个派(fruit):\n",
    "    \"\"\" 英文差，不用怕 \"\"\"\n",
    "    π = \"派\"\n",
    "    return fruit + π\n",
    "\n",
    "print(加个派(\"苹果\"))\n",
    "\n",
    "开心 = \"😁\"\n",
    "print(开心)\n",
    "print('hehe')\n",
    "# 😔 = \"sad\"    # 但是不能用 emoji 表情\n",
    "\n",
    "class T(object):\n",
    "    pass\n",
    "\n",
    "issubclass(T, object)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\"\"\" Division\n",
    "In Python 3, / is float division\n",
    "\n",
    "In Python 2, / is integer division (assuming int inputs), \n",
    "\n",
    "In both 2 and 3, // is integer division\n",
    "\"\"\"\n",
    "\n",
    "print(3 / 2)    # python2 use \"from __future__ import division\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'a': <class 'float'>, 'b': <class 'int'>, 'return': <class 'float'>}\n",
      "[0, 1, 1, 2, 3, 5, 8]\n"
     ]
    }
   ],
   "source": [
    "# Function annotations (Python 3.5 (PEP 484))\n",
    "\n",
    "# Python doesn't do anything with the annotations other than put them in an\n",
    "# __annotations__ dictionary.\n",
    "\n",
    "def add(a: float, b: int = 1) -> float:\n",
    "    \"\"\" 函数注解不作真正的类型检查，只是为 IDE 等工具提供方便，可以增强类型提示功能 \"\"\"\n",
    "    return a + b\n",
    "\n",
    "add(42) \n",
    "\n",
    "print(add.__annotations__)\n",
    "\n",
    "# 如果想实现真正的类型检查，可以尝试下 \n",
    "# Mypy: Optional Static Typing for Python\n",
    "\n",
    "from typing import Iterator\n",
    "def fib(n: int) -> Iterator[int]:\n",
    "    a, b = 0, 1\n",
    "    while a < n:\n",
    "        yield a\n",
    "        a, b = b, a + b\n",
    "\n",
    "print(list(fib(10)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true,
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "# Streamlined super()\n",
    "\n",
    "import json\n",
    "class Config(object):\n",
    "    def __init__(self, config_dict):\n",
    "        self.data = config_dict\n",
    "\n",
    "class ClientConfig(Config):\n",
    "    def __init__(self, json_config_file):\n",
    "        with open(json_config_file) as fh:\n",
    "            data = json.load(fh)\n",
    "        super(ClientConfig, self).__init__(data)\n",
    "        \n",
    "class ClientConfig(Config):\n",
    "    def __init__(self, json_config_file):\n",
    "        with open(json_config_file) as fh:\n",
    "            data = json.load(fh)\n",
    "        super().__init__(data)  # <---- Shorter!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: 0\n",
      "b: 1\n",
      "rest: [2, 3, 4, 5, 6, 7, 8, 9]\n",
      "rest: [0, 1, 2, 3, 4, 5, 6, 7, 8]\n",
      "1234\n"
     ]
    }
   ],
   "source": [
    "# Feature: Advanced unpacking\n",
    "\n",
    "a, b, *rest = range(10)\n",
    "print('a:', a)    # print 在 python 3 中成为函数\n",
    "print('b:', b)\n",
    "print('rest:', rest)\n",
    "\n",
    "a, *rest, b = range(10) \n",
    "\n",
    " \n",
    "*rest, b = range(10)\n",
    "print('rest:', rest)\n",
    "\n",
    "with open(\"LICENSE\") as f:\n",
    "    # 这样我们可以很 happy 地获取一个文件的首尾行\n",
    "    first, *_, last = f.readlines()\n",
    "    \n",
    "dict1 = {'a':1, 'b': 2}\n",
    "dict2 = {'a':3, 'c': 4}\n",
    "\n",
    "print(1234)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "sum() takes 2 positional arguments but 3 were given",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-7-e1ab9d7e94bb>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     20\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     21\u001b[0m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbiteme\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 22\u001b[0;31m \u001b[0msum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m    \u001b[0;31m# 调用失败\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: sum() takes 2 positional arguments but 3 were given"
     ]
    }
   ],
   "source": [
    "# Feature: Keyword only arguments\n",
    "\n",
    "def f(a, b, *, option=True):\n",
    "    # 一定程度上避免 **kwargs 的使用，兼容 api \n",
    "    pass\n",
    "\n",
    "def sum(a, b, biteme=False):\n",
    "    if biteme:\n",
    "        shutil.rmtree('./')    # dangerous !!!\n",
    "    else:\n",
    "        return a + b\n",
    "    \n",
    "# sum(1, 2, 3)     # dangerous !!!\n",
    "\n",
    "def sum(a, b, *, biteme=False):\n",
    "    if biteme:\n",
    "        shutil.rmtree('./')    # dangerous !!!\n",
    "    else:\n",
    "        return a + b\n",
    "     \n",
    "sum(1, 2, biteme=False)\n",
    "sum(1, 2, False)    # 调用失败"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "ename": "NotImplementedError",
     "evalue": "automatic sudo injection",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mOSError\u001b[0m                                   Traceback (most recent call last)",
      "\u001b[0;31mOSError\u001b[0m: ",
      "\nThe above exception was the direct cause of the following exception:\n",
      "\u001b[0;31mNotImplementedError\u001b[0m                       Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-8-486ee9667cfc>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     10\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m \u001b[0mmycopy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'old'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'new'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<ipython-input-8-486ee9667cfc>\u001b[0m in \u001b[0;36mmycopy\u001b[0;34m(source, dest)\u001b[0m\n\u001b[1;32m      7\u001b[0m         \u001b[0mshutil\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcopy2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msource\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdest\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      8\u001b[0m     \u001b[0;32mexcept\u001b[0m \u001b[0mOSError\u001b[0m\u001b[0;34m:\u001b[0m  \u001b[0;31m# python2 里 raise 会丢失原来的 traceback 信息\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m         \u001b[0;32mraise\u001b[0m \u001b[0mNotImplementedError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"automatic sudo injection\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mOSError\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     10\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mNotImplementedError\u001b[0m: automatic sudo injection"
     ]
    }
   ],
   "source": [
    "# Chained exceptions\n",
    "\n",
    "import shutil\n",
    "\n",
    "def mycopy(source, dest):\n",
    "    try:\n",
    "        shutil.copy2(source, dest)\n",
    "    except OSError:  # python2 里 raise 会丢失原来的 traceback 信息\n",
    "        raise NotImplementedError(\"automatic sudo injection\") from OSError\n",
    "        \n",
    "        \n",
    "mycopy('old', 'new') "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Everything is an iterator\n",
    "\n",
    "- In Python 3, range, zip, map, dict.values, etc. are all iterators. No xrange, dict.iteritems anymore.\n",
    "\n",
    "- If you want a list, just wrap the result with list.\n",
    "\n",
    "- Explicit is better than implicit.\n",
    "\n",
    "- Harder to write code that accidentally uses too much memory, because the input was bigger than you expected."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "unorderable types: int() < str()",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-5-cdb4dbe8af63>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;31m# python2 里不同类型的值可以直接比较，结果比较诡异，同时可能会导致一些非预期结果\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;36m42\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;34m'42'\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;36m1\u001b[0m    \u001b[0;31m# python3 不再支持不同类型数据比较，避免隐藏错误\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: unorderable types: int() < str()"
     ]
    }
   ],
   "source": [
    "# No more comparison of everything to everything\n",
    "# python2 里不同类型的值可以直接比较，结果比较诡异，同时可能会导致一些非预期结果\n",
    "\n",
    "42 < '42' and None > 1    # python3 不再支持不同类型数据比较，避免隐藏错误"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
      "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
      "[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]\n"
     ]
    }
   ],
   "source": [
    "# yield from\n",
    "\n",
    "def gen():\n",
    "    for i in range(10):\n",
    "        yield i\n",
    "\n",
    "def f():\n",
    "    for i in gen():  # python2 \n",
    "        yield i\n",
    "\n",
    "def f2():\n",
    "    yield from gen() # py3, easily refactor generators into subgenerators.\n",
    "   \n",
    "print(list(f()))\n",
    "print(list(f2()))\n",
    "\n",
    "\n",
    "def dup(n):\n",
    "    for i in range(n):\n",
    "        yield from (i, i)\n",
    "        \n",
    "print(list(dup(10)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "task2 sleeping for: 3 seconds\n",
      "task1 sleeping for: 4 seconds\n",
      "task3 sleeping for: 2 seconds\n",
      "task3 is finished\n",
      "task2 is finished\n",
      "task1 is finished\n"
     ]
    }
   ],
   "source": [
    "# asyncio — Asynchronous I/O, event loop, coroutines and tasks (new in 3.4)\n",
    "# https://docs.python.org/3/library/asyncio-task.html \n",
    "\n",
    "import asyncio\n",
    " \n",
    "@asyncio.coroutine\n",
    "def my_coroutine(task_name, seconds_to_sleep=3):\n",
    "    print('{0} sleeping for: {1} seconds'.format(task_name, seconds_to_sleep))\n",
    "    yield from asyncio.sleep(seconds_to_sleep)\n",
    "    print('{0} is finished'.format(task_name))\n",
    " \n",
    "loop = asyncio.new_event_loop()\n",
    "asyncio.set_event_loop(loop)\n",
    "\n",
    "tasks = [\n",
    "    my_coroutine('task1', 4),\n",
    "    my_coroutine('task2', 3),\n",
    "    my_coroutine('task3', 2)\n",
    "]\n",
    "loop.run_until_complete(asyncio.wait(tasks))\n",
    "loop.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "URL: http://www.baidu.com:  Content-Length: 102125\n",
      "URL: http://www.sohu.com:  Content-Length: 133744\n",
      "URL: http://www.zhihu.com:  Content-Length: 9190\n",
      "URL: http://www.douban.com:  Content-Length: 91588\n"
     ]
    }
   ],
   "source": [
    "# aiohttp\n",
    "\n",
    "import asyncio\n",
    "import aiohttp\n",
    " \n",
    "@asyncio.coroutine\n",
    "def fetch_page(url):\n",
    "    response = yield from aiohttp.request('GET', url)\n",
    "    assert response.status == 200\n",
    "    content = yield from response.read()\n",
    "    print('URL: {0}:  Content-Length: {1}'.format(url, len(content)))\n",
    " \n",
    "loop = asyncio.new_event_loop() \n",
    "asyncio.set_event_loop(loop)\n",
    "\n",
    "tasks = [\n",
    "    fetch_page('http://www.zhihu.com'),\n",
    "    fetch_page('http://www.baidu.com'),\n",
    "    fetch_page('http://www.sohu.com'),\n",
    "    fetch_page('http://www.douban.com'),\n",
    "]\n",
    "loop.run_until_complete(asyncio.wait(tasks))\n",
    "loop.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "URL: http://www.sohu.com  Content-Length: 133744\n",
      "URL: http://www.baidu.com  Content-Length: 102163\n",
      "URL: http://www.zhihu.com  Content-Length: 9190\n",
      "URL: http://www.douban.com  Content-Length: 91588\n"
     ]
    }
   ],
   "source": [
    "# python3.5 新增了 async/await 语法\n",
    "\n",
    "import asyncio\n",
    "import aiohttp\n",
    " \n",
    "async def fetch_page(url):\n",
    "    response = await aiohttp.request('GET', url)\n",
    "    assert response.status == 200\n",
    "    content = await response.read()\n",
    "    print('URL: {0}  Content-Length: {1}'.format(url, len(content)))\n",
    " \n",
    "loop = asyncio.new_event_loop()\n",
    "asyncio.set_event_loop(loop)\n",
    "\n",
    "tasks = [\n",
    "    fetch_page('http://www.zhihu.com'),\n",
    "    fetch_page('http://www.baidu.com'),\n",
    "    fetch_page('http://www.sohu.com'),\n",
    "    fetch_page('http://www.douban.com'),\n",
    "]\n",
    "loop.run_until_complete(asyncio.wait(tasks))\n",
    "loop.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Standard library additions\n",
    "\n",
    "- asyncio\n",
    "- ipaddress\n",
    "- functools.lru_cache\n",
    "- enum\n",
    "- pathlib\n",
    "- mock\n",
    "- concurrent.futures\n",
    "- math.isclose    # compare float number\n",
    "- lzma, for an improved compression compared to gzip\n",
    "- the `__pycache__` directory, that helps to avoid littering every other project folder with .pyc files\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Can I use Python3\n",
    "\n",
    "- 历史遗留问题，目前使用 python3 的项目比较少\n",
    "- [http://py3readiness.org/](http://py3readiness.org/)，主流 python 库都已经支持 python3\n",
    "- 上一个项目用了 python3.5.2 + flask 全家桶，生产环境未见坑\n",
    "- Python 社区在 2020 年将停止维护 python2，社区正努力推进 python3\n",
    "- Django, IPython 已宣布不久将停止 python2 支持\n",
    "- 新项目、小项目或者内部项目可以尝试下 python3\n",
    "- Today, services built on Python 3.5 using asyncio are widely used at Facebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "\"\"\"\n",
    "Today, services built on Python 3.5 using asyncio are widely used at Facebook. \n",
    "But as recently as May of 2014 it was actually impossible to use Python 3 at Facebook. \n",
    "Come learn how we cut the Gordian Knot of dependencies and social aversion to the point where new services \n",
    "are now being written in Python 3 and existing codebases have plans to move to Python 3.5.\n",
    "\"\"\"\n",
    "from IPython.display import YouTubeVideo\n",
    "YouTubeVideo('gR73nLbbgqY')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# 编写 python2 和 python3 兼容代码\n",
    "\n",
    "- 可以尝试编写 2 和 3 兼容代码保证代码迁移\n",
    "- [six 模块](https://pythonhosted.org/six/)\n",
    "- [2to3 代码转换工具](https://docs.python.org/2/library/2to3.html)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": true,
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "import six\n",
    "\n",
    "def dispatch_types(value):\n",
    "    if isinstance(value, six.integer_types):\n",
    "        handle_integer(value)\n",
    "    elif isinstance(value, six.class_types):\n",
    "        handle_class(value)\n",
    "    elif isinstance(value, six.string_types):\n",
    "        handle_string(value)\n",
    "\n",
    "\n",
    "from six import with_metaclass\n",
    "\n",
    "class Meta(type):\n",
    "    pass\n",
    "\n",
    "class Base(object):\n",
    "    pass\n",
    "\n",
    "class MyClass(with_metaclass(Meta, Base)):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> Python3 大法好，退 Python2 保平安 </center>\n",
    "<br>\n",
    "<center>![2vs3](http://img.blog.csdn.net/20161222203627297?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbm9yYXdhbmdzaXl1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)</center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# 参考链接\n",
    "\n",
    "- [six](https://pythonhosted.org/six/)\n",
    "- [2to3](https://docs.python.org/2/library/2to3.html)\n",
    "- [diveintopython3](http://www.diveintopython3.net/)\n",
    "- [What's New In Python 3.5](https://docs.python.org/3/whatsnew/3.5.html)\n",
    "- [What's New In Python 3.6](https://docs.python.org/3/whatsnew/3.6.html)\n",
    "- [What's REALLY New in Python 3](https://powerfulpython.com/blog/whats-really-new-in-python-3/)\n",
    "- [Porting Code to Python 3 with 2to3](http://www.diveintopython3.net/porting-code-to-python-3-with-2to3.html)\n",
    "- [Simple Python Version Management: pyenv](https://github.com/pyenv/pyenv)"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
